<template>
  <div class="codemirror-input-wrap">
    <codemirror
      :class="{'valid-failed':codeValidFailed}"
      :options="options"
      @input="onInput"
      @blur="onBlur"
      @ready="onReady"
      @cursorActivity="cursorActivity"
      class="codemirror-input"
      ref="codemirrorInput"
      :value="codeContent"
      :placeholder="placeholder"
    ></codemirror>
    <slot></slot>
  </div>
</template>

<script>
import { codemirror } from 'vue-codemirror'
import 'codemirror/lib/codemirror.css'

// placeholder
import 'codemirror/addon/display/placeholder.js'
import 'codemirror/theme/blackboard.css'
// 代码高亮
import 'codemirror/mode/sql/sql.js'
import 'codemirror/mode/xml/xml.js'
import 'codemirror/mode/javascript/javascript.js'
import 'codemirror/mode/groovy/groovy.js'
import 'codemirror/mode/shell/shell.js'
// 自动闭合标签
import 'codemirror/addon/edit/closetag.js'

// 行高亮
import 'codemirror/addon/selection/active-line.js'

// styleSelectedText
import 'codemirror/addon/selection/mark-selection.js'
import 'codemirror/addon/search/searchcursor.js'

// highlightSelectionMatches
import 'codemirror/addon/scroll/annotatescrollbar.js'
import 'codemirror/addon/search/matchesonscrollbar.js'
// 上面highlightSelectionMatches已引入
// import 'codemirror/addon/search/searchcursor.js'
import 'codemirror/addon/search/match-highlighter.js'

// keyMap
import 'codemirror/mode/clike/clike.js'
import 'codemirror/addon/edit/matchbrackets.js'
import 'codemirror/addon/comment/comment.js'
import 'codemirror/addon/dialog/dialog.js'
import 'codemirror/addon/dialog/dialog.css'
// 上面highlightSelectionMatches已引入
// import 'codemirror/addon/search/searchcursor.js'
import 'codemirror/addon/search/search.js'
import 'codemirror/keymap/sublime.js'

// foldGutter
import 'codemirror/addon/fold/foldgutter.css'
import 'codemirror/addon/fold/brace-fold.js'
import 'codemirror/addon/fold/comment-fold.js'
import 'codemirror/addon/fold/foldcode.js'
import 'codemirror/addon/fold/foldgutter.js'
import 'codemirror/addon/fold/indent-fold.js'
import 'codemirror/addon/fold/markdown-fold.js'
import 'codemirror/addon/fold/xml-fold.js'

// 自动补齐
import 'codemirror/addon/hint/show-hint.js'
import 'codemirror/addon/hint/show-hint.css'
import 'codemirror/addon/hint/anyword-hint.js'
import 'codemirror/addon/hint/css-hint.js'
import 'codemirror/addon/hint/javascript-hint.js'
import 'codemirror/addon/hint/sql-hint.js'
import 'codemirror/addon/hint/html-hint.js'
import 'codemirror/addon/hint/xml-hint.js'

export default {
  name: 'crudCodemirrorInput',
  model: {
    prop: 'codeContent',
    event: 'change'
  },
  components: { codemirror },
  props: {
    height: {
      type: String,
      default: '300px'
    },
    readOnly: {
      type: Boolean,
      default: false
    },
    placeholder: {
      type: String,
      default: '请输入内容'
    },
    codeContent: String,
    // 语言 ['js', 'ts', 'json', 'groovy', 'xml', 'sql']
    lang: {
      type: String,
      default: 'xml'
    },
    // 行号
    lineNumbers: {
      type: Boolean,
      default: true
    },
    line: {
      type: Boolean,
      default: true
    },
    // 自动闭合标签(xml,html)
    autoCloseTags: Boolean,
    styleActiveLine: {
      type: Boolean,
      default: true
    },
    // 空格缩进数
    indentUnit: {
      type: Number,
      default: 2
    },
    dark: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      codeValidFailed: false
    }
  },
  watch: {
    height (nv) {
      if (nv) {
        this.initHeight(nv)
      }
    }
  },
  computed: {
    options () {
      let mode = ''
      const lang = this.lang.toLocaleLowerCase()
      switch (lang) {
        case 'xml':
          mode = 'application/xml'
          break
        case 'shell':
          mode = 'application/x-sh'
          break
        case 'sql':
          mode = 'text/x-sql'
          break
        case 'js':
          mode = 'text/javascript'
          break
        case 'ts':
          mode = 'text/typescript'
          break
        case 'json':
          mode = 'application/json'
          break
        case 'groovy':
          mode = 'text/x-groovy'
          break
        default:
          mode = 'application/xml'
      }
      return {
        tabSize: 2,
        mode,
        theme: this.dark ? 'blackboard' : 'default',
        readOnly: this.readOnly,
        lineNumbers: this.lineNumbers,
        line: this.line,
        autoRefresh: true,
        lineWrapping: true,
        autoCloseTags: !!this.autoCloseTags || ['xml', 'html'].includes(this.lang.toLocaleLowerCase()),
        styleActiveLine: this.styleActiveLine,
        // 选中文本
        styleSelectedText: true,
        indentUnit: this.indentUnit,
        // 代码折叠
        foldGutter: true,
        gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
        // 快捷键 [sublime, emacs, vim]
        keyMap: 'sublime',
        // 匹配 {} () []
        matchBrackets: true,
        // 正在选中时是否显示光标
        showCursorWhenSelecting: true,
        // 相同单词高亮
        highlightSelectionMatches: { showToken: /\w/, annotateScrollbar: true },
        hintOptions: {
          // 当匹配只有一项的时候是否自动补全
          completeSingle: false
        },
        extraKeys: {
          // tab用空格缩进
          Ctrl: 'autocomplete',
          Tab: function (cm) {
            if (cm.somethingSelected()) {
              cm.indentSelection('add')
            } else {
              cm.replaceSelection(Array(cm.getOption('indentUnit') + 1).join(' '), 'end', '+input')
              // cm.replaceSelection(cm.getOption(indentUnit) ? Array(cm.getOption('indentUnit') + 1).join(' ') : '\t', 'end', '+input')
            }
          },
          // 反向缩进
          'Shift-Tab': (cm) => {
            if (cm.somethingSelected()) {
              cm.indentSelection('subtract')
            } else {
              // 直接缩进整行
              cm.indentLine(cm.getCursor().line, 'subtract')
            }
          }
        }
      }
    }
  },
  mounted () {
    this.initHeight()
  },
  methods: {
    initHeight (data) {
      this.$nextTick(() => {
        this.$refs.codemirrorInput.$el.children[1].style = `height:${data || this.height};`
      })
    },
    onInput (value) {
      this.$emit('change', value)
      this.$emit('on-change', value)
    },
    onBlur (value) {
      this.$emit('on-blur', value)
    },
    onReady (value) {
      this.$emit('on-ready', value)
    },
    cursorActivity (cm) {
      // cm.showHint()
    }
  }
}
</script>

<style lang="scss" scoped>
  .codemirror-input-wrap {
    position: relative;
  }

  .codemirror-input {
    position: relative;
    border: 1px solid #dcdee2;
    line-height: 1.5;

    .ivu-form-item-error & {
      border: 1px solid $--color-danger;
    }
  }

  .explain {
    line-height: 1.5;
    font-style: italic;
    padding: 5px 0;
    color: #a2a1a1;
  }
</style>

<style lang="scss">
  .codemirror-input {
    overflow-y: auto;

    &.valid-failed {
      > input,
      > .ivu-select-selection,
      &.vue-codemirror.cm {
        border: 1px solid $--color-danger;
      }
    }

    .CodeMirror {
      // height: auto;
    }

    .cm-matchhighlight {
      background-color: lightgreen;
    }
  }

  pre.CodeMirror-placeholder.CodeMirror-line-like {
    color: #999;
  }
</style>
